package top.ijiujiu.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;
import top.ijiujiu.entity.SysUser;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtTokenUtil
 *
 * @Author 神秘的黄瓜
 * @Version 1.0
 * @Date 2020-06-16 16:50
 */
public class JwtTokenUtil {


    public static final String TOKEN_HEADER = "Authorization";

    public static final String TOKEN_PREFIX = "Bearer ";

    // 密钥
    private static final String SECRET = "e491c2a869347ef7d8aff4a62b6000e0";

    // JWT签发者
    private static final String ISS = "pxl";

    // 过期时间 毫秒
    public static final Long EXPIRATION = 1000 * 60 * 30L;

    // 自定义属性
    public static final String ROLES = "roles";

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

    private JwtTokenUtil() {

    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private static String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET).compact();
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private static Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    /**
     * 生成令牌
     *
     * @param user
     * @return
     */
    public static String generateToken(SysUser user) throws IOException {
        Map<String, Object> claims = new HashMap<>(4);
        claims.put(Claims.ISSUER, ISS);
        try {
            claims.put(Claims.SUBJECT, OBJECT_MAPPER.writeValueAsString(user));
        } catch (JsonProcessingException e) {
            throw new IOException("创建Token失败!");
        }
        claims.put(Claims.ISSUED_AT, new Date());
        claims.put(ROLES, StringUtils.collectionToCommaDelimitedString(user.getAuthorities()));
        return generateToken(claims);
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public static Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public static String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put(Claims.ISSUED_AT, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 验证令牌
     *
     * @param token 令牌
     * @param user  用户
     * @return 是否有效
     */
    public static Boolean validateToken(String token, SysUser user) {
        SysUser tokenUser = getUserFromToken(token);
        return (tokenUser.getUsername().equals(user.getUsername()) && !isTokenExpired(token));
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户
     */
    public static SysUser getUserFromToken(String token) {
        SysUser user = null;
        try {
            Claims claims = getClaimsFromToken(token);
            user = OBJECT_MAPPER.readValue(claims.getSubject(), SysUser.class);
        } catch (Exception e) {
            user = null;
        }
        return user;
    }


    /**
     * 从令牌中获取角色信息
     *
     * @param token 令牌
     * @return 角色信息
     */
    public static String getRolesFromToken(String token) {
        String roles;
        try {
            Claims claims = getClaimsFromToken(token);
            roles = claims.get(ROLES, String.class);
        } catch (Exception e) {
            roles = null;
        }
        return roles;
    }
}
